None
Анализ взаимосвязей и особенностей заведений общественного питания Москвы:
# импортируем библиотеки
import missingno as msno
import pandas as pd
import numpy as np
import math
import matplotlib.pyplot as plt
import seaborn as sns
import plotly.express as px
from plotly import graph_objects as go
import folium
import json
from folium import Map, Choropleth
# посмотрим на первые строки
display(data.head())
| name | category | address | district | hours | lat | lng | rating | price | avg_bill | middle_avg_bill | middle_coffee_cup | chain | seats | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | WoWфли | кафе | Москва, улица Дыбенко, 7/1 | Северный административный округ | ежедневно, 10:00–22:00 | 55.878494 | 37.478860 | 5.0 | NaN | NaN | NaN | NaN | 0 | NaN |
| 1 | Четыре комнаты | ресторан | Москва, улица Дыбенко, 36, корп. 1 | Северный административный округ | ежедневно, 10:00–22:00 | 55.875801 | 37.484479 | 4.5 | выше среднего | Средний счёт:1500–1600 ₽ | 1550.0 | NaN | 0 | 4.0 |
| 2 | Хазри | кафе | Москва, Клязьминская улица, 15 | Северный административный округ | пн-чт 11:00–02:00; пт,сб 11:00–05:00; вс 11:00... | 55.889146 | 37.525901 | 4.6 | средние | Средний счёт:от 1000 ₽ | 1000.0 | NaN | 0 | 45.0 |
| 3 | Dormouse Coffee Shop | кофейня | Москва, улица Маршала Федоренко, 12 | Северный административный округ | ежедневно, 09:00–22:00 | 55.881608 | 37.488860 | 5.0 | NaN | Цена чашки капучино:155–185 ₽ | NaN | 170.0 | 0 | NaN |
| 4 | Иль Марко | пиццерия | Москва, Правобережная улица, 1Б | Северный административный округ | ежедневно, 10:00–22:00 | 55.881166 | 37.449357 | 5.0 | средние | Средний счёт:400–600 ₽ | 500.0 | NaN | 1 | 148.0 |
# отобразим основную инвормацию
display(data.info())
<class 'pandas.core.frame.DataFrame'> RangeIndex: 8406 entries, 0 to 8405 Data columns (total 14 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 name 8406 non-null object 1 category 8406 non-null object 2 address 8406 non-null object 3 district 8406 non-null object 4 hours 7870 non-null object 5 lat 8406 non-null float64 6 lng 8406 non-null float64 7 rating 8406 non-null float64 8 price 3315 non-null object 9 avg_bill 3816 non-null object 10 middle_avg_bill 3149 non-null float64 11 middle_coffee_cup 535 non-null float64 12 chain 8406 non-null int64 13 seats 4795 non-null float64 dtypes: float64(6), int64(1), object(7) memory usage: 919.5+ KB
None
# построим столбцатую диаграмму количества пропусков
msno.bar(data)
plt.show()
# посчитаем количество полных дубликатов
print(f'Количество полных дубликотов в данных равно {data.duplicated().sum()}')
print('*****************************************')
print('Количество дубликотов по имени и координатах в данных равно:', data[['name','lat','lng']].duplicated().sum())
Количество полных дубликотов в данных равно 0 ***************************************** Количество дубликотов по имени и координатах в данных равно: 0
# # отобразим уникальные значения для проверки на ошибки и неявные дубликаты
display(data['name'].unique()[:50])
# посчитаем количество уникальных заведений
print(f'количество уникальных заведений в городе Москва равно:', len(data['name'].unique()))
array(['WoWфли', 'Четыре комнаты', 'Хазри', 'Dormouse Coffee Shop',
'Иль Марко', 'Sergio Pizza', 'Огни города', 'Mr. Уголёк',
'Donna Maria', 'Готика', 'Great Room Bar', 'Шашлык Шефф',
'Заправка', 'Буханка', 'У Сильвы', 'Дом обеда', 'База Стритфуд',
'Чайхана Беш-Бармак', 'Час-Пик', 'Пекарня', 'Чебуреки Манты',
'7/12', 'Крымские чебуреки', 'Drive Café', 'В парке вкуснее',
'Пикочино', 'Шаурму Х@чу', 'Mafe', 'Кушай Город', 'Кафедра',
'Алталия', 'Додо Пицца', 'Изба', "Домино'с Пицца", 'Виладж пицца',
'Пекарня № 1 Грузинская кухня', 'Халяль закусочная', 'Ижора',
'Шаурмагия', 'Кафе', 'Крошка Картошка', 'Варня кафе',
'Суши & пицца', 'Кафетерий', '9 Bar Coffee', 'CofeFest',
'БлинБери', '2U-Ту-Ю', 'Шаурма', 'Арамье'], dtype=object)
количество уникальных заведений в городе Москва равно: 5614
# отобрази топ по количеству заведений
plt.style.use('seaborn-whitegrid')
data.groupby(by='name')['name'].count().sort_values(
ascending=False).head(20).plot(kind='bar',
figsize=(15, 5),
alpha=0.6,
color='g')
plt.title('количечтво заведений в Москве')
plt.ylabel('количество')
plt.xlabel('названия заведений')
plt.xticks(rotation=37)
plt.show()
print('**********************************************************')
display(
data.groupby(by='name')['name'].count().sort_values(
ascending=False).head(20))
C:\Users\coder\AppData\Local\Temp\ipykernel_4180\3802653872.py:2: MatplotlibDeprecationWarning: The seaborn styles shipped by Matplotlib are deprecated since 3.6, as they no longer correspond to the styles shipped by seaborn. However, they will remain available as 'seaborn-v0_8-<style>'. Alternatively, directly use the seaborn API instead.
plt.style.use('seaborn-whitegrid')
**********************************************************
name Кафе 189 Шоколадница 120 Домино'с Пицца 76 Додо Пицца 74 One Price Coffee 71 Яндекс Лавка 69 Cofix 65 Prime 50 Хинкальная 44 Шаурма 43 КОФЕПОРТ 42 Кулинарная лавка братьев Караваевых 39 Теремок 38 Чайхана 37 Ресторан 34 CofeFest 32 Буханка 32 Столовая 28 Му-Му 27 Drive Café 24 Name: name, dtype: int64
# # отобразим уникальные значения для проверки на ошибки и неявные дубликаты
data['category'].unique()
array(['кафе', 'ресторан', 'кофейня', 'пиццерия', 'бар,паб',
'быстрое питание', 'булочная', 'столовая'], dtype=object)
data['category'] = data['category'].astype('category')
# готовим данные для графика
category = pd.DataFrame(data['category'].value_counts()).reset_index()
# строим диаграмму с сегментами
fig = go.Figure(data=[go.Pie(labels=category['index'], # указываем значения, которые появятся на метках сегментов
values=category['category'],# указываем данные, которые отобразятся на графике
)]) # добавляем аргумент, который выделит сегмент-лидер на графике
fig.update_layout(title='Число заведений в зависимости от их категории', # указываем заголовок графика
width=800, # указываем размеры графика
height=600,
annotations=[dict(x=1.12, # вручную настраиваем аннотацию легенды
y=1.05,
text='Категория заведения',
showarrow=False)])
fig.show() # выводим график
# отобразим уникальные значения для проверки на ошибки и неявные дубликаты
data['address'].unique()[:50]
array(['Москва, улица Дыбенко, 7/1', 'Москва, улица Дыбенко, 36, корп. 1',
'Москва, Клязьминская улица, 15',
'Москва, улица Маршала Федоренко, 12',
'Москва, Правобережная улица, 1Б', 'Москва, Ижорская улица, вл8Б',
'Москва, Клязьминская улица, 9, стр. 3',
'Москва, Дмитровское шоссе, 107, корп. 4',
'Москва, Ангарская улица, 39', 'Москва, Левобережная улица, 12',
'Москва, улица Маршала Федоренко, 10с1',
'Москва, МКАД, 80-й километр, 1',
'Москва, Базовская улица, 15, корп. 1',
'Москва, Ангарская улица, 42с1',
'Москва, улица Бусиновская Горка, 2',
'Москва, Базовская улица, 15, корп. 8',
'Москва, Ленинградское шоссе, 71Б, стр. 2',
'Москва, Коровинское шоссе, 30А', 'Москва, Ижорский проезд, 5',
'Москва, Прибрежный проезд, 7',
'Москва, Коровинское шоссе, 35, стр. 17',
'Москва, Лобненская улица, 13к2', 'Москва, улица Дыбенко, 9Ас1',
'Москва, парк Левобережный', 'Москва, Дмитровское шоссе, 107к2',
'Москва, улица Дыбенко, 7, стр. 1',
'Москва, МКАД, 78-й километр, 14к1',
'Москва, Дмитровское шоссе, 157, стр. 15',
'Москва, Базовская улица, 15, корп. 6',
'Москва, Коровинское шоссе, 23, корп. 1',
'Москва, Лобненская улица, 4А',
'Москва, Клязьминская улица, 11, корп. 4',
'Москва, Базовская улица, 15, корп. 15',
'Москва, Коровинское шоссе, 33А',
'Москва, Коровинское шоссе, 46, стр. 5',
'Москва, Ижорский проезд, 5А', 'Москва, Базовская улица, 15А',
'Москва, Ижорская улица, 18, стр. 1',
'Москва, Коровинское шоссе, 29, корп. 1',
'Москва, Дмитровское шоссе, 169, корп. 6',
'Москва, Ангарская улица, 24А',
'Москва, Коровинское шоссе, 41, стр. 1',
'Москва, улица Маршала Федоренко, 6с1',
'Москва, улица Маршала Федоренко, 7', 'Москва, Ижорская улица, 8А',
'Москва, улица Дыбенко, 44', 'Москва, Ангарская улица, 30/25',
'Москва, Дмитровское шоссе, 151, корп. 5',
'Москва, Угличская улица, 13, стр. 8',
'Москва, Абрамцевская улица, 30, стр. 1'], dtype=object)
len(data['address'].unique())
5753
# напишем функцию
def od(s):
"""This function take string wiht address and return name of street"""
try:
st = s.split(',')[1]
st = ''.join(st.split(' улица'))
st = ''.join(st.split('улица '))
return st.strip()
except:
'err'
print(od('Москва, Дмитровское шоссе, 151, корп. 5'))
Дмитровское шоссе
# применем
data['street'] = data['address'].apply(od)
print(f"Количество уникальных названий улиц равно: {len(data['street'].unique())}")
Количество уникальных названий улиц равно: 1448
# посмотрим на неявные дубликаты
data['street'].unique()[:50]
array(['Дыбенко', 'Клязьминская', 'Маршала Федоренко', 'Правобережная',
'Ижорская', 'Дмитровское шоссе', 'Ангарская', 'Левобережная',
'МКАД', 'Базовская', 'Бусиновская Горка', 'Ленинградское шоссе',
'Коровинское шоссе', 'Ижорский проезд', 'Прибрежный проезд',
'Лобненская', 'парк Левобережный', 'Угличская', 'Абрамцевская',
'ландшафтный заказник Лианозовский', 'Череповецкая',
'Алтуфьевское шоссе', 'Лианозовский парк культуры и отдыха',
'Дубнинская', 'Илимская', 'Шенкурский проезд', 'Новгородская',
'парк Алтуфьево', 'Пришвина', 'бульвар Академика Ландау',
'Лескова', 'Плещеева', 'Бибиревская', 'Лианозовский проезд',
'Челобитьевское шоссе', 'Костромская', 'Карельский бульвар',
'Инженерная', 'парк Ангарские пруды', 'Софьи Ковалевской',
'Корнейчука', 'Проектируемый проезд № 5265', 'Римского-Корсакова',
'800-летия Москвы', 'Юрловский проезд', 'парк Ангарские Пруды',
'Вагоноремонтная', 'Мурановская', 'Конёнкова', 'Широкая'],
dtype=object)
# посчитаем топ
top_street = data.groupby('street')[['street']].count()
top_street.columns = ['point_count']
# отсортируем и оставим пять лидеров
top_street = top_street.reset_index().sort_values(by='point_count', ascending=False).head(15)
top_street.columns = ['Улица','Количество заведений']
display(top_street)
| Улица | Количество заведений | |
|---|---|---|
| 1440 | проспект Мира | 184 |
| 1038 | Профсоюзная | 122 |
| 1437 | проспект Вернадского | 108 |
| 696 | Ленинский проспект | 107 |
| 693 | Ленинградский проспект | 95 |
| 479 | Дмитровское шоссе | 88 |
| 589 | Каширское шоссе | 77 |
| 358 | Варшавское шоссе | 76 |
| 694 | Ленинградское шоссе | 70 |
| 731 | МКАД | 65 |
| 725 | Люблинская | 60 |
| 353 | Вавилова | 55 |
| 683 | Кутузовский проспект | 54 |
| 806 | Миклухо-Маклая | 49 |
| 1048 | Пятницкая | 48 |
# строим столбчатую диаграмму
fig = px.bar(top_street.sort_values(by='Количество заведений',ascending=True),
x='Количество заведений', # указываем столбец с данными для оси X
y='Улица', # указываем столбец с данными для оси Y
text='Улица',color='Количество заведений',color_continuous_scale='ylorbr'# добавляем аргумент, который отобразит текст с информацией # о количестве объявлений внутри столбца графика
)
# оформляем график
fig.update_layout(title='ТОП-15 улиц по количеству заведений',
xaxis_title='Количество заведений',
yaxis_title='Улица'
)
fig.update_yaxes(title='y', visible=False, showticklabels=False)
fig.show() # выводим график
# отобразим уникальные значения для проверки на ошибки и неявные дубликаты
data.district.unique()
array(['Северный административный округ',
'Северо-Восточный административный округ',
'Северо-Западный административный округ',
'Западный административный округ',
'Центральный административный округ',
'Восточный административный округ',
'Юго-Восточный административный округ',
'Южный административный округ',
'Юго-Западный административный округ'], dtype=object)
# приведем к верному типу
data.district = data.district.astype('category')
print('Общее количество заведений в Москве равно:',len(data))
Общее количество заведений в Москве равно: 8406
# готовим данные для графика
district = pd.DataFrame(data['district'].value_counts()).reset_index()
# строим диаграмму с сегментами
fig = go.Figure(data=[go.Pie(labels=district['index'], # указываем значения, которые появятся на метках сегментов
values=district['district'],# указываем данные, которые отобразятся на графике
)]) # добавляем аргумент, который выделит сегмент-лидер на графике
fig.update_layout(title='Число заведений в зависимости от принадлежности к административному округу', # указываем заголовок графика
width=800, # указываем размеры графика
height=600,
annotations=[dict(x=1.12, # вручную настраиваем аннотацию легенды
y=1.05,
text='Округ города',
showarrow=False)])
fig.show() # выводим график
print('**********************************************')
display(district)
**********************************************
| index | district | |
|---|---|---|
| 0 | Центральный административный округ | 2242 |
| 1 | Северный административный округ | 900 |
| 2 | Южный административный округ | 892 |
| 3 | Северо-Восточный административный округ | 891 |
| 4 | Западный административный округ | 851 |
| 5 | Восточный административный округ | 798 |
| 6 | Юго-Восточный административный округ | 714 |
| 7 | Юго-Западный административный округ | 709 |
| 8 | Северо-Западный административный округ | 409 |
# отобразим уникальные значения для проверки на ошибки и неявные дубликаты
display(data.hours.unique()[:20])
array(['ежедневно, 10:00–22:00',
'пн-чт 11:00–02:00; пт,сб 11:00–05:00; вс 11:00–02:00',
'ежедневно, 09:00–22:00', 'ежедневно, 10:00–23:00',
'пн 15:00–04:00; вт-вс 15:00–05:00',
'пн-чт 10:00–22:00; пт,сб 10:00–23:00; вс 10:00–22:00',
'ежедневно, 12:00–00:00', 'ежедневно, круглосуточно',
'ежедневно, 10:00–21:00', 'вт-сб 09:00–18:00',
'ежедневно, 08:00–22:00', 'ежедневно, 13:00–00:00',
'пн-пт 08:30–18:30; сб 10:00–20:00', 'ежедневно, 09:00–21:00',
'пн-пт 09:00–21:00',
'пн-чт 11:00–22:00; пт,сб 11:00–23:00; вс 11:00–22:00',
'пн-пт 08:00–22:00; сб,вс 10:00–22:00', 'ежедневно, 10:00–19:00',
'пн-пт 09:00–16:00', 'ежедневно, 08:00–21:00'], dtype=object)
# отобразим топ популярных графиков работы
data.value_counts('hours').head(10).plot(kind='bar',figsize=(15,5),alpha=0.6,color='g')
plt.title('Топ 10 популярных графиков работы заведений в Москве')
plt.ylabel('Количество')
plt.xlabel('График работы')
plt.xticks(rotation=37)
plt.show();
print('**********************************************************')
display(data.value_counts('hours').head(10))
**********************************************************
hours ежедневно, 10:00–22:00 759 ежедневно, круглосуточно 730 ежедневно, 11:00–23:00 396 ежедневно, 10:00–23:00 310 ежедневно, 12:00–00:00 254 ежедневно, 09:00–21:00 204 ежедневно, 09:00–22:00 184 ежедневно, 12:00–23:00 178 ежедневно, 08:00–23:00 160 ежедневно, 08:00–22:00 148 dtype: int64
# Выделим и посчитаем количество заведений с графиком 24/7, создадим отдельный столбец с булевым значением отрожающий этот факт
# посмотрим на результат фильтрации
display(data.loc[((data['hours'].str.contains("ежедневно, круглосуточно"))|(data['hours'].str.contains("круглосуточно, ежедневно")))\
&(~data['hours'].isna())].head())
print('*********************************************')
print('Количество заведений с графиком работы 24/7 равно:',len(data.loc[((data['hours'].str.contains("ежедневно, круглосуточно"))|(data['hours'].str.contains("круглосуточно, ежедневно")))\
&(~data['hours'].isna())]))
| name | category | address | district | hours | lat | lng | rating | price | avg_bill | middle_avg_bill | middle_coffee_cup | chain | seats | street | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 10 | Great Room Bar | бар,паб | Москва, Левобережная улица, 12 | Северный административный округ | ежедневно, круглосуточно | 55.877832 | 37.469171 | 4.5 | средние | Цена бокала пива:250–350 ₽ | NaN | NaN | 0 | 102.0 | Левобережная |
| 17 | Чайхана Беш-Бармак | ресторан | Москва, Ленинградское шоссе, 71Б, стр. 2 | Северный административный округ | ежедневно, круглосуточно | 55.876908 | 37.449876 | 4.4 | средние | Средний счёт:350–500 ₽ | 425.0 | NaN | 0 | 96.0 | Ленинградское шоссе |
| 19 | Пекарня | булочная | Москва, Ижорский проезд, 5 | Северный административный округ | ежедневно, круглосуточно | 55.887969 | 37.515688 | 4.4 | NaN | NaN | NaN | NaN | 1 | NaN | Ижорский проезд |
| 24 | Drive Café | кафе | Москва, улица Дыбенко, 9Ас1 | Северный административный округ | ежедневно, круглосуточно | 55.879992 | 37.481571 | 4.0 | NaN | NaN | NaN | NaN | 1 | NaN | Дыбенко |
| 49 | 2U-Ту-Ю | пиццерия | Москва, Ижорская улица, 8А | Северный административный округ | ежедневно, круглосуточно | 55.886160 | 37.508784 | 2.7 | NaN | Средний счёт:900 ₽ | 900.0 | NaN | 0 | NaN | Ижорская |
********************************************* Количество заведений с графиком работы 24/7 равно: 730
# все верно взглянем на строки с пропусками в этом столбце
display(data.loc[(data['hours'].isna())].tail());
display(data.loc[(data['hours'].isna())].head())
| name | category | address | district | hours | lat | lng | rating | price | avg_bill | middle_avg_bill | middle_coffee_cup | chain | seats | street | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 8236 | 1у | кафе | Москва, Нагатинская набережная, 40/1к1 | Южный административный округ | NaN | 55.685528 | 37.673546 | 3.4 | NaN | NaN | NaN | NaN | 0 | NaN | Нагатинская набережная |
| 8375 | Улица Гурьянова 55 | кафе | Москва, улица Гурьянова, 55 | Юго-Восточный административный округ | NaN | 55.679981 | 37.717034 | 4.5 | NaN | NaN | NaN | NaN | 0 | NaN | Гурьянова |
| 8378 | Восточно-грузинская кухня | быстрое питание | Москва, Зеленодольская улица, 32, корп. 3 | Юго-Восточный административный округ | NaN | 55.710540 | 37.767864 | 4.3 | NaN | NaN | NaN | NaN | 0 | 120.0 | Зеленодольская |
| 8381 | Аэлита | кафе | Москва, Ферганская улица, 8, корп. 2, стр. 1 | Юго-Восточный административный округ | NaN | 55.708871 | 37.803831 | 3.8 | NaN | NaN | NaN | NaN | 0 | 30.0 | Ферганская |
| 8395 | Истира Запрафка | кафе | Москва, Юго-Восточный административный округ, ... | Юго-Восточный административный округ | NaN | 55.724686 | 37.710558 | 2.9 | NaN | NaN | NaN | NaN | 0 | NaN | Юго-Восточный административный округ |
| name | category | address | district | hours | lat | lng | rating | price | avg_bill | middle_avg_bill | middle_coffee_cup | chain | seats | street | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 38 | Ижора | булочная | Москва, Ижорский проезд, 5А | Северный административный округ | NaN | 55.888366 | 37.514856 | 4.4 | NaN | NaN | NaN | NaN | 0 | NaN | Ижорский проезд |
| 40 | Кафе | кафе | Москва, Ижорская улица, 18, стр. 1 | Северный административный округ | NaN | 55.895115 | 37.524902 | 3.7 | NaN | NaN | NaN | NaN | 0 | NaN | Ижорская |
| 44 | Кафетерий | кафе | Москва, Ангарская улица, 24А | Северный административный округ | NaN | 55.876289 | 37.519315 | 3.8 | NaN | NaN | NaN | NaN | 1 | 8.0 | Ангарская |
| 56 | Рыба из тандыра | быстрое питание | Москва, Коровинское шоссе, 46, стр. 5 | Северный административный округ | NaN | 55.888010 | 37.515960 | 1.5 | NaN | NaN | NaN | NaN | 0 | NaN | Коровинское шоссе |
| 108 | Кафе | бар,паб | Москва, МКАД, 82-й километр, вл18 | Северо-Восточный административный округ | NaN | 55.908930 | 37.558777 | 4.2 | NaN | NaN | NaN | NaN | 0 | NaN | МКАД |
ничего необычного, просто отсутсвует финансовая онформация, однако для обшей статистики много полезного. Возможно сказавается дополненеи сета из разных источников
# добавим столбец новый 'is_24/7'
# заполним положительные значения
data['is_24/7'] = data.loc[(data['hours'].str.contains("ежедневно, круглосуточно"))&(~data['hours'].isna())]['hours'] ==\
data.loc[(data['hours'].str.contains("ежедневно, круглосуточно"))&(~data['hours'].isna())]['hours']
# заполним отрицательные значения
data.loc[~(data['hours'].isna())&(data['is_24/7'].isna()),'is_24/7'] = False
# выполним проверку
len(data[data['is_24/7']==False])
7140
Значение координат обычно добавляются автоматически системой, поэтом подробно смотреть эти столбцы нет смысла, взглянем лишь на их описание во збежание серьезных ошибок
print('широта',data.lat.describe());
print("********************************")
print('долгота',data.lng.describe());
широта count 8406.000000 mean 55.750109 std 0.069658 min 55.573942 25% 55.705155 50% 55.753425 75% 55.795041 max 55.928943 Name: lat, dtype: float64 ******************************** долгота count 8406.000000 mean 37.608570 std 0.098597 min 37.355651 25% 37.538583 50% 37.605246 75% 37.664792 max 37.874466 Name: lng, dtype: float64
все в пределах нормы, идем дальше
# отобразим уникальные значения для проверки на ошибки и неявные дубликаты
display(data.rating.unique())
array([5. , 4.5, 4.6, 4.4, 4.7, 4.8, 4.3, 4.9, 4.2, 4.1, 4. , 3.8, 3.9,
3.7, 3.6, 2.8, 2.7, 3.1, 1.5, 2. , 1.4, 3.3, 3.5, 3.2, 2.9, 3. ,
3.4, 2.3, 2.2, 2.5, 2.6, 1.7, 1. , 1.1, 2.4, 1.3, 1.2, 2.1, 1.8,
1.9, 1.6])
# построим гистограмму для отображения распределения среднего рейтинга
sns.set_style('darkgrid')
plt.figure(figsize=(10,6))
ax = sns.histplot(x='rating', data=data, kde=True,bins=33)
plt.grid(True)
ax.set_title('Распределения рейтингов')
ax.set_ylabel('Количество')
ax.set_xlabel('Рейтинг')
plt.show();
print('*****************************************')
print(data.rating.describe())
***************************************** count 8406.000000 mean 4.229895 std 0.470348 min 1.000000 25% 4.100000 50% 4.300000 75% 4.400000 max 5.000000 Name: rating, dtype: float64
# # отобразим уникальные значения для проверки на ошибки и неявные дубликаты
display(data.price.unique())
# приведем к категориальному типу
data['price']= data['price'].astype('category')
array([nan, 'выше среднего', 'средние', 'высокие', 'низкие'], dtype=object)
print('Количество пропусков в колонке price равно', data['price'].isna().sum() )
Количество пропусков в колонке price равно 5091
# отобразим Количество заведений с по ценовым категориям для
data.value_counts('price').plot(kind='bar',figsize=(15,5),alpha=0.6,color='g')
plt.title('Количество заведений по ценовым категориям')
plt.ylabel('Количество')
plt.xlabel('Ценовая категория')
plt.xticks(rotation=37)
plt.show();
print('**********************************************************')
display(data.value_counts('price'))
**********************************************************
price средние 2117 выше среднего 564 высокие 478 низкие 156 dtype: int64
# отобразим уникальные значения для проверки на ошибки и неявные дубликаты
display(data.avg_bill.unique()[:40])
array([nan, 'Средний счёт:1500–1600 ₽', 'Средний счёт:от 1000 ₽',
'Цена чашки капучино:155–185 ₽', 'Средний счёт:400–600 ₽',
'Средний счёт:199 ₽', 'Средний счёт:200–300 ₽',
'Средний счёт:от 500 ₽', 'Средний счёт:1000–1200 ₽',
'Цена бокала пива:250–350 ₽', 'Средний счёт:330 ₽',
'Средний счёт:1500 ₽', 'Средний счёт:300–500 ₽',
'Средний счёт:140–350 ₽', 'Средний счёт:350–500 ₽',
'Средний счёт:300–1500 ₽', 'Средний счёт:от 240 ₽',
'Средний счёт:200–250 ₽', 'Средний счёт:328 ₽',
'Средний счёт:300 ₽', 'Средний счёт:от 345 ₽',
'Средний счёт:60–400 ₽', 'Средний счёт:900 ₽',
'Средний счёт:500–800 ₽', 'Средний счёт:500–1000 ₽',
'Средний счёт:600–700 ₽', 'Цена бокала пива:120–350 ₽',
'Средний счёт:1000–1500 ₽', 'Средний счёт:1500–2000 ₽',
'Цена чашки капучино:150–190 ₽', 'Средний счёт:2000–2500 ₽',
'Средний счёт:600 ₽', 'Средний счёт:450 ₽',
'Цена чашки капучино:120–170 ₽', 'Средний счёт:100–500 ₽',
'Средний счёт:от 850 ₽', 'Цена чашки капучино:100–200 ₽',
'Средний счёт:250–600 ₽', 'Средний счёт:2100 ₽',
'Средний счёт:349 ₽'], dtype=object)
явных ошибок и дубликатов не видно
print('Количество пропусков в колонке avg_bill равно', data['avg_bill'].isna().sum() )
Количество пропусков в колонке avg_bill равно 4590
# отобразим топ 15 самызх популярных значений среднего счета
data.avg_bill.value_counts().head(15).plot(kind='bar',figsize=(15,5),alpha=0.6,color='g')
plt.title('Топ 15 показателей средного счета')
plt.ylabel('Количество')
plt.xlabel('Средний счет')
plt.xticks(rotation=37)
plt.show();
print('**********************************************************')
display(data.avg_bill.value_counts().head(15))
**********************************************************
Средний счёт:1000–1500 ₽ 241 Средний счёт:1500–2000 ₽ 120 Средний счёт:300–500 ₽ 90 Средний счёт:500–1000 ₽ 78 Средний счёт:1500–2500 ₽ 68 Средний счёт:700–1000 ₽ 50 Средний счёт:от 1500 ₽ 48 Средний счёт:1500 ₽ 43 Средний счёт:1000 ₽ 43 Цена чашки капучино:239–274 ₽ 43 Средний счёт:1000–2000 ₽ 43 Средний счёт:300 ₽ 42 Средний счёт:1000–1200 ₽ 42 Средний счёт:от 500 ₽ 41 Средний счёт:200–300 ₽ 41 Name: avg_bill, dtype: int64
# посмотрим на значения не выделеные в отдельный столбец
data.loc[(~data['avg_bill'].isna())&(data['middle_coffee_cup'].isna())&(data['middle_avg_bill'].isna())]['avg_bill'][:10]
10 Цена бокала пива:250–350 ₽ 67 Цена бокала пива:120–350 ₽ 97 Цена бокала пива:90–230 ₽ 241 Цена бокала пива:160–499 ₽ 417 Цена бокала пива:199–300 ₽ 418 Цена бокала пива:от 140 ₽ 421 Цена бокала пива:от 149 ₽ 424 Цена бокала пива:150–450 ₽ 814 Цена бокала пива:130–150 ₽ 1030 Цена бокала пива:220–350 ₽ Name: avg_bill, dtype: object
print('Количество пропусков в колонке middle_avg_bill равно', data['middle_avg_bill'].isna().sum() )
Количество пропусков в колонке middle_avg_bill равно 5257
# построим гистограмму для отображения распределения среднего чека
sns.set_style('darkgrid')
plt.figure(figsize=(10,6))
ax = sns.histplot(x='middle_avg_bill', data=data, kde=True)
plt.xlim(1,4000)
plt.grid(True)
ax.set_title('Распределения сумм среднего чека')
ax.set_ylabel('Количество')
ax.set_xlabel('Сумма')
plt.show();
print('*****************************************')
print(data.middle_avg_bill.describe())
***************************************** count 3149.000000 mean 958.053668 std 1009.732845 min 0.000000 25% 375.000000 50% 750.000000 75% 1250.000000 max 35000.000000 Name: middle_avg_bill, dtype: float64
print('Количество пропусков в колонке middle_coffee_cup равно', data['middle_coffee_cup'].isna().sum() )
Количество пропусков в колонке middle_coffee_cup равно 7871
# построим гистограмму для отображения распределения средней стоимости чашки капучино
plt.figure(figsize=(10,6))
ax = sns.histplot(x='middle_coffee_cup', data=data, kde=True,bins=250)
sns.set_style('darkgrid')
plt.xlim(0,400)
plt.grid(True)
ax.set_title('Распределения сумм средней стоимости чашки капучино')
ax.set_ylabel('Количество')
ax.set_xlabel('Сумма')
plt.show();
print('*****************************************')
print(data.middle_coffee_cup.describe())
***************************************** count 535.000000 mean 174.721495 std 88.951103 min 60.000000 25% 124.500000 50% 169.000000 75% 225.000000 max 1568.000000 Name: middle_coffee_cup, dtype: float64
# посмотрим на уникальные значения
display(data.seats.unique()[:40])
array([ nan, 4., 45., 148., 79., 65., 102., 180., 96., 25., 46.,
40., 247., 21., 8., 35., 240., 85., 12., 60., 120., 16.,
80., 50., 44., 43., 90., 0., 198., 95., 350., 124., 10.,
70., 48., 6., 98., 30., 52., 20.])
print('Количество пропусков в колонке seats равно', data['seats'].isna().sum() )
Количество пропусков в колонке seats равно 3611
# построим гистограмму для отображения распределения количеста посадочных мест среди заведений
plt.figure(figsize=(10,6))
ax = sns.histplot(x='seats', data=data, kde=True,bins=400)
sns.set_style('darkgrid')
plt.xlim(0,400)
plt.grid(True)
ax.set_title('Распределения количеста посадочных мест среди заведений')
ax.set_ylabel('Количество')
ax.set_xlabel('Количество посадочных мест')
plt.show();
print('*****************************************')
print(data.seats.describe())
***************************************** count 4795.000000 mean 108.421689 std 122.833396 min 0.000000 25% 40.000000 50% 75.000000 75% 140.000000 max 1288.000000 Name: seats, dtype: float64
# отобразим уникальные значения для проверки на ошибки и неявные дубликаты
display(data.chain.unique())
array([0, 1], dtype=int64)
# выделим не еденичные заведения маркированные как несетевые
b = data.loc[data['chain']==0].groupby(by='name')['name'].count().sort_values()
print('Количество не еденичных заведений маркерованых несетевыми',len(b.loc[b>1]))
Количество не еденичных заведений маркерованых несетевыми 16
# выделим еденичные заведения маркированные как сетевые
k = data.loc[data['chain']==1].groupby(by='name')['name'].count().sort_values()
print('Количество еденичных заведений маркерованых сетевыми',len(k.loc[k==1]))
Количество еденичных заведений маркерованых сетевыми 64
# выделем и посмотрим на эти заведения
k = k.loc[k==1]
is_not_chain = data.query("name in @k.index")
display(is_not_chain.head())
display(is_not_chain.groupby(by='category')['name'].count());
| name | category | address | district | hours | lat | lng | rating | price | avg_bill | middle_avg_bill | middle_coffee_cup | chain | seats | street | is_24/7 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 133 | Крепери | бар,паб | Москва, улица Лескова, 14 | Северо-Восточный административный округ | ежедневно, 10:00–22:00 | 55.897490 | 37.604722 | 4.0 | NaN | NaN | NaN | NaN | 1 | NaN | Лескова | False |
| 206 | Vintage | кафе | Москва, Дмитровское шоссе, 163Ак1 | Северо-Восточный административный округ | ежедневно, 10:00–22:00 | 55.908956 | 37.540036 | 4.1 | NaN | NaN | NaN | NaN | 1 | NaN | Дмитровское шоссе | False |
| 382 | Halal food | кафе | Москва, Петрозаводская улица, 34 | Северный административный округ | ежедневно, круглосуточно | 55.867428 | 37.489429 | 4.4 | средние | Средний счёт:200–400 ₽ | 300.0 | NaN | 1 | 30.0 | Петрозаводская | True |
| 613 | Wаурма | кафе | Москва, улица Героев Панфиловцев, 1/2 | Северо-Западный административный округ | NaN | 55.852060 | 37.438371 | 4.1 | NaN | NaN | NaN | NaN | 1 | NaN | Героев Панфиловцев | NaN |
| 745 | В своей тарелке | столовая | Москва, Локомотивный проезд, 21 | Северный административный округ | NaN | 55.845414 | 37.573537 | 3.9 | NaN | NaN | NaN | NaN | 1 | NaN | Локомотивный проезд | NaN |
category бар,паб 5 булочная 1 быстрое питание 5 кафе 30 кофейня 6 пиццерия 5 ресторан 9 столовая 3 Name: name, dtype: int64
# выделем и посмотрим на эти заведения
b = b.loc[b>1]
is_chain = data.query("name in @b.index")
display(is_chain.head())
display(is_chain.groupby(by='category')['name'].count());
| name | category | address | district | hours | lat | lng | rating | price | avg_bill | middle_avg_bill | middle_coffee_cup | chain | seats | street | is_24/7 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 40 | Кафе | кафе | Москва, Ижорская улица, 18, стр. 1 | Северный административный округ | NaN | 55.895115 | 37.524902 | 3.7 | NaN | NaN | NaN | NaN | 0 | NaN | Ижорская | NaN |
| 47 | Кафе | кафе | Москва, улица Маршала Федоренко, 7 | Северный административный округ | ежедневно, 11:00–00:00 | 55.880306 | 37.489760 | 2.8 | NaN | NaN | NaN | NaN | 0 | 35.0 | Маршала Федоренко | False |
| 50 | Шаурма | быстрое питание | Москва, улица Дыбенко, 44 | Северный административный округ | ежедневно, круглосуточно | 55.878339 | 37.483154 | 3.9 | NaN | NaN | NaN | NaN | 0 | 40.0 | Дыбенко | True |
| 108 | Кафе | бар,паб | Москва, МКАД, 82-й километр, вл18 | Северо-Восточный административный округ | NaN | 55.908930 | 37.558777 | 4.2 | NaN | NaN | NaN | NaN | 0 | NaN | МКАД | NaN |
| 123 | Кафе | кафе | Москва, Шенкурский проезд, 14 | Северо-Восточный административный округ | NaN | 55.897794 | 37.591395 | 4.3 | NaN | NaN | NaN | NaN | 0 | 50.0 | Шенкурский проезд | NaN |
category бар,паб 4 булочная 3 быстрое питание 45 кафе 210 кофейня 15 пиццерия 4 ресторан 44 столовая 40 Name: name, dtype: int64
# отобразим соотношение сетевых и не сетевых заведений
chain = data.chain.value_counts().reset_index()
chain.index = ['не является сетевым','сетевое']
chain.columns = ['Категория','Количество']
chain.plot(kind='bar',figsize=(9,5),alpha=0.6,color='g')
plt.title('Соотношение сетевых и не сетевых заведений')
plt.ylabel('Количество')
plt.xlabel('Тип заведения')
plt.legend('')
plt.xticks(rotation=37)
plt.show();
print('**********************************************************')
display(chain)
**********************************************************
| Категория | Количество | |
|---|---|---|
| не является сетевым | 0 | 5201 |
| сетевое | 1 | 3205 |
# готовим данные для графика
category = pd.DataFrame(data['category'].value_counts()).reset_index()
# строим диаграмму с сегментами
fig = go.Figure(data=[go.Pie(labels=category['index'], # указываем значения, которые появятся на метках сегментов
values=category['category'],# указываем данные, которые отобразятся на графике
)])
fig.update_layout(title='Число заведений в зависимости от их категории', # указываем заголовок графика
width=800, # указываем размеры графика
height=600,
annotations=[dict(x=1.12, # вручную настраиваем аннотацию легенды
y=1.05,
text='Категория заведения',
showarrow=False)])
fig.show() # выводим график
# подготовим выборку для топа заведений по категориям
top_cat_name_all = data.groupby(by=['category','name']).count().sort_values(by='lat',ascending=False)['lat'].reset_index()
# подпишем колонки и выделим топ 5
top_cat_name_all.columns = ['Категория','Название','Количество заведений']
top_cat_name_good = top_cat_name_all.loc[top_cat_name_all['Категория'] == 'кафе'].head(5).reset_index(drop=True)
# повторим для остольных категорий
for cat in ['бар,паб', 'булочная', 'быстрое питание', 'кофейня', 'пиццерия', 'ресторан', 'столовая']:
top_cat_name = top_cat_name_all.loc[top_cat_name_all['Категория'] == cat].head(5).reset_index(drop=True)
top_cat_name_good = pd.concat([top_cat_name_good,top_cat_name])
# добавим правки для графика
top_cat_name_good.loc[(top_cat_name_good['Категория'] == 'столовая')&(top_cat_name_good['Название'] == 'Кафе'),'Название'] = 'Кафе_'
top_cat_name_good.loc[(top_cat_name_good['Категория'] == 'быстрое питание')&(top_cat_name_good['Название'] == 'Кафе'),'Название'] = '_Кафе_'
# строим график
fig = px.bar(top_cat_name_good, x='Название', y='Количество заведений',\
color='Категория', title='Топ 5 заведений по каличеству точек в разных категориях',text_auto='.2s')
fig.update_xaxes(tickangle=45)
fig.update_layout(width=1050, height=800)
fig.show()
# сделаем функцию для создания нового столбца
def ne(s):
try:
if s == 0:
return 'несетевые'
else:
return 'сетевые'
except:
err
# применим функцию
data['new_chain'] = data['chain'].apply(ne)
sns.set_style('darkgrid')
plt.figure(figsize=(15,6))
ax = sns.violinplot(y='seats', x ='category', data=data,hue='new_chain',split=True)
sns.move_legend(ax, "lower right", bbox_to_anchor=(1, 1.01), ncol=3, title=None, frameon=False)
plt.title('Распределения рейтингов по категориям')
plt.title('Распределения значения количества посадочных месть по категориям')
plt.xlabel('')
plt.ylim(-100,450)
plt.ylabel('Количество посадочных мест')
plt.show();
display('СЕТЕВЫЕ',data.loc[data['new_chain']=='сетевые'].groupby(by='category')['seats'].median().sort_values(ascending=False));
print('********************')
display('НЕСЕТЕВЫЕ',data.loc[data['new_chain']=='несетевые'].groupby(by='category')['seats'].median().sort_values(ascending=False))
'СЕТЕВЫЕ'
category ресторан 98.0 кофейня 90.0 бар,паб 80.0 столовая 80.0 быстрое питание 75.0 кафе 70.0 пиццерия 52.0 булочная 50.0 Name: seats, dtype: float64
********************
'НЕСЕТЕВЫЕ'
category бар,паб 85.0 ресторан 80.0 столовая 66.0 кофейня 60.0 пиццерия 60.0 быстрое питание 56.0 кафе 55.0 булочная 50.0 Name: seats, dtype: float64
# выделим данные
top_cat_name_seats = data.pivot_table(index=['category','name'],values='seats',aggfunc=['count','median']).reset_index()
# подпишем колонки, отсортируем и уберем дубликаты
top_cat_name_seats.columns = ['категория','название заведения','количество заведений','медианное количество посадочных мест']
top_cat_name_seats = top_cat_name_seats.sort_values(by=['количество заведений','категория'],ascending=False)
top_cat_name_seats = top_cat_name_seats.drop_duplicates(subset=['категория', 'название заведения'], keep='last')
# соберем топ 5 по каличеству точек для каждой котегории
top_cat_name_seats_good = pd.DataFrame()
for cat in ['кафе','бар,паб', 'булочная', 'быстрое питание', 'кофейня', 'пиццерия', 'ресторан', 'столовая']:
top_cat_name_seats_ = top_cat_name_seats.loc[top_cat_name_seats['категория'] == cat].sort_values(by='количество заведений',\
ascending=False).head(5)
top_cat_name_seats_good = pd.concat([top_cat_name_seats_good,top_cat_name_seats_])
# поправим одноименные названия для графика
top_cat_name_seats_good.loc[(top_cat_name_seats_good['категория'] == 'столовая')\
&(top_cat_name_seats_good['название заведения'] == 'Кафе'),'название заведения'] = 'Кафе_'
top_cat_name_seats_good = top_cat_name_seats_good.sort_values(by='медианное количество посадочных мест',ascending=False)
# строим график
fig = px.bar(top_cat_name_seats_good, x='название заведения', y='медианное количество посадочных мест',\
color='категория', title='Медианные значения количества посадочных мест в топ 5-ти заведений по каличеству точек',\
text_auto='.2s')
fig.update_xaxes(tickangle=45)
fig.update_layout(width=950, height=700)
fig.show()
# подготовим данные
category = pd.DataFrame(data['chain'].value_counts()).reset_index()
# переиминуем колонки
category.columns = ['Тип заведения','Число заведений']
category.loc[category['Тип заведения']==0,'Тип заведения'] = 'несетевые'
category.loc[category['Тип заведения']==1,'Тип заведения'] = 'сетевые'
# строим диаграмму с сегментами
fig = go.Figure(data=[go.Pie(labels=category['Тип заведения'], # указываем значения, которые появятся на метках сегментов
values=category['Число заведений'],# указываем данные, которые отобразятся на графике
)])
fig.update_layout(title='Число заведений в зависимости от их типа', # указываем заголовок графика
width=700, # указываем размеры графика
height=500,
annotations=[dict(x=1.12,
y=1.05,
text='Тип заведения',
showarrow=False)]
)
fig.show() # выводим график
# подготовим выборку для топа заведений по категориям
top_cat_chain = data.groupby(by=['new_chain','category']).count().sort_values(by='lat',ascending=False)['lat'].reset_index()
# подпишем колонки и выделим топ 5
top_cat_chain.columns = ['Тип','Категория','Количество заведений']
top_cat_chain_good = top_cat_chain.loc[top_cat_chain['Категория'] == 'кафе'].head(5).reset_index(drop=True)
# повторим для остольных категорий
for cat in ['бар,паб', 'булочная', 'быстрое питание', 'кофейня', 'пиццерия', 'ресторан', 'столовая']:
top_cat = top_cat_chain.loc[top_cat_chain['Категория'] == cat].head(5).reset_index(drop=True)
top_cat_chain_good = pd.concat([top_cat_chain_good,top_cat])
# строим график
fig = px.bar(top_cat_chain_good.sort_values(by='Количество заведений',ascending=False), x='Категория', y='Количество заведений',\
color='Тип', title='Соотношение в топ 5 заведениях по каличеству точек сетевых и несетевых заведений',text_auto='.2s')
fig.update_xaxes(tickangle=45)
fig.update_layout(width=950, height=700)
fig.show();
# посчитаем топы
top_name = data.groupby(by='name')['name'].count().sort_values(ascending=False).head(15)
# выделим данные для дополнительной информации
top_name1 = data.query("name in @top_name.index")
top_name1 = top_name1.drop_duplicates(subset=['name'],keep='last')
top_name1 = top_name1.reset_index(drop=True)
# сформируем итоговою таблицу
top_name = pd.DataFrame(top_name)
top_name.columns = ['Количество']
top_name.reset_index(drop=False, inplace=True)
top_name.columns = ['name','Количество заведений']
# добавим информацию о категориях и типе заведения
top_name_all = pd.merge(top_name,top_name1,on=["name","name"])
top_name['Категория'] = top_name_all['category']
top_name['Тип'] = top_name_all['new_chain']
top_name.columns = ['Название', 'Количество заведений', 'Категория', 'Тип']
# построим графиг для наглядного отображения результатов
fig = px.bar(top_name.sort_values(by='Количество заведений',ascending=True), x='Количество заведений', y='Название',\
color='Тип', title='Топ 15 заведений по каличеству точек в разных категориях',text='Категория')
fig.update_xaxes(tickangle=45)
fig.update_yaxes(tickangle=20)
fig.update_layout(width=1000, height=700)
fig.show();
# сделаем фенкцию правки значений district для графика
def cleaner(s):
s = ''.join(s.split(' административный округ'))
return s
cleaner('Северный административный округ')
'Северный'
# подготовим данные
data['district_good'] = data['district'].apply(cleaner)
district = data.groupby(by=['district_good','category'])['name'].count().reset_index()
district.columns = ['Административный округ','Категория','Количество заведений']
# построим графиг для наглядного отображения результатов
fig = px.bar(district.sort_values(by='Количество заведений',ascending=True), x='Административный округ', y='Количество заведений',\
color='Категория', title='Распределение заведений по Административным округам с учетом категорий',text='Категория')
fig.update_xaxes(tickangle=45)
fig.update_layout(width=1000, height=900)
fig.show();
sns.set_style('darkgrid')
plt.figure(figsize=(15,6))
ax = sns.violinplot(y='rating', x ='category', data=data,hue='new_chain',split=True)
sns.move_legend(ax, "lower right", bbox_to_anchor=(1, 1.01), ncol=3, title=None, frameon=False)
plt.title('Распределения рейтингов по категориям')
plt.xlabel('')
plt.ylim(3,5.1)
plt.ylabel('Рейтинг')
plt.show();
display('СЕТЕВЫЕ',data.loc[data['new_chain']=='сетевые'].groupby(by='category')['rating'].median().sort_values(ascending=False));
print('********************')
display('НЕСЕТЕВЫЕ',data.loc[data['new_chain']=='несетевые'].groupby(by='category')['rating'].median().sort_values(ascending=False))
'СЕТЕВЫЕ'
category бар,паб 4.40 булочная 4.30 кафе 4.30 пиццерия 4.30 ресторан 4.30 столовая 4.25 быстрое питание 4.20 кофейня 4.20 Name: rating, dtype: float64
********************
'НЕСЕТЕВЫЕ'
category бар,паб 4.4 кофейня 4.4 ресторан 4.4 булочная 4.3 пиццерия 4.3 столовая 4.3 быстрое питание 4.2 кафе 4.2 Name: rating, dtype: float64
# выделим данные
top_cat_name_rating = data.pivot_table(index=['category','name'],values='rating',aggfunc=['count','median']).reset_index()
# подпишем колонки, отсортируем и уберем дубликаты
top_cat_name_rating.columns = ['категория','название заведения','количество заведений','медианное количество рейтинга']
top_cat_name_rating = top_cat_name_rating.sort_values(by=['количество заведений','категория'],ascending=False)
top_cat_name_rating = top_cat_name_rating.drop_duplicates(subset=['категория', 'название заведения'], keep='last')
# соберем топ 5 по каличеству точек для каждой котегории
top_cat_name_rating_good = pd.DataFrame()
for cat in ['кафе','бар,паб', 'булочная', 'быстрое питание', 'кофейня', 'пиццерия', 'ресторан', 'столовая']:
top_cat_name_rating_ = top_cat_name_rating.loc[top_cat_name_rating['категория'] == cat].sort_values(by='количество заведений',\
ascending=False).head(5)
top_cat_name_rating_good = pd.concat([top_cat_name_rating_good,top_cat_name_rating_])
# поправим одноименные названия для графика
top_cat_name_rating_good.loc[(top_cat_name_rating_good['категория'] == 'столовая')\
&(top_cat_name_rating_good['название заведения'] == 'Кафе'),'название заведения'] = 'Кафе_'
top_cat_name_rating_good.loc[(top_cat_name_rating_good['категория'] == 'быстрое питание')\
&(top_cat_name_rating_good['название заведения'] == 'Кафе'),'название заведения'] = '_Кафе_'
top_cat_name_rating_good = top_cat_name_rating_good.sort_values(by='медианное количество рейтинга',ascending=False)
# строим график
fig = px.bar(top_cat_name_rating_good, x='название заведения', y='медианное количество рейтинга',\
color='категория', title='Медианные значения рейтинга в топ 5-ти заведений по каличеству точек',\
text_auto='.2s')
fig.update_xaxes(tickangle=45)
fig.update_layout(width=950, height=580)
fig.show()
# посчитаем срединй рейтинг для каждого округа
rating_df = data.groupby('district', as_index=False)['rating'].agg('median')
display(rating_df)
| district | rating | |
|---|---|---|
| 0 | Восточный административный округ | 4.3 |
| 1 | Западный административный округ | 4.3 |
| 2 | Северный административный округ | 4.3 |
| 3 | Северо-Восточный административный округ | 4.2 |
| 4 | Северо-Западный административный округ | 4.3 |
| 5 | Центральный административный округ | 4.4 |
| 6 | Юго-Восточный административный округ | 4.2 |
| 7 | Юго-Западный административный округ | 4.3 |
| 8 | Южный административный округ | 4.3 |
# отобразам результат на карте москвы
import json
from folium import Map, Choropleth
try:
state_geo = 'F:\\обучение\\projects\\moskow_restourants\\admin_level_geomap.geojson'
except:
state_geo = 'https://code.s3.yandex.net/data-analyst/admin_level_geomap.geojson'
state_geo = 'https://code.s3.yandex.net/data-analyst/admin_level_geomap.geojson'
moscow_lat, moscow_lng = 55.751244, 37.618423
# создаём карту Москвы
m = folium.Map(location=[moscow_lat, moscow_lng])
# выводим карту
# создаём хороплет с помощью конструктора Choropleth и добавляем его на карту
Choropleth(
geo_data=state_geo,
data=rating_df,
columns=['district', 'rating'],
key_on='feature.name',
fill_color='Blues',
fill_opacity=0.8,
legend_name='Медианный рейтинг заведений по районам',
).add_to(m)
# выводим карту
m
import math
# напишем функцию
point = [55.751244, 37.618423]
def distance(origin):
lat1, lon1 = origin['lat'],origin['lng']
point = [55.751244, 37.618423]
lat2, lon2 = point
radius = 6371 # km
dlat = math.radians(lat2-lat1)
dlon = math.radians(lon2-lon1)
a = math.sin(dlat/2) * math.sin(dlat/2) + math.cos(math.radians(lat1)) \
* math.cos(math.radians(lat2)) * math.sin(dlon/2) * math.sin(dlon/2)
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a))
d = radius * c
return round(d,3)
# применим финкцию и создадим два новых столбца с расстояниями для дальнейшего анализа
data['distance'] = data.apply(distance,axis=1)
data['distance_km'] = round(data['distance'],0)
# подготовим иконки для разных категорий
icon_cafe = 'https://img.icons8.com/external-victoruler-solid-victoruler/64/null/external-cafe-buildings-victoruler-solid-victoruler.png'
icon_caffee_house = 'https://img.icons8.com/ios-filled/50/null/cafe-building.png'
icone_pizza = 'https://img.icons8.com/external-kiranshastry-solid-kiranshastry/64/null/external-pizza-food-kiranshastry-solid-kiranshastry.png'
icone_bar = 'https://img.icons8.com/pastel-glyph/64/null/cocktail--v2.png'
icote_fast_food = 'https://img.icons8.com/external-nawicon-glyph-nawicon/64/null/external-fast-food-food-delivery-nawicon-glyph-nawicon.png'
icote_backery = 'https://img.icons8.com/ios-filled/50/null/bakery.png'
icone_dinning_room = 'https://img.icons8.com/external-goofy-solid-kerismaker/96/null/external-Dinning-Table-Set-furniture-goofy-solid-kerismaker.png'
icone_restourant = 'https://img.icons8.com/sf-black-filled/64/null/restaurant-building.png'
# соберем для нанесения на карту
icons = {'кафе':icon_cafe, 'кофейня':icon_caffee_house,'пиццерия':icone_pizza,'бар,паб':icone_bar,\
'быстрое питание':icote_fast_food,'булочная':icote_backery,'столовая':icone_dinning_room,'ресторан':icone_restourant}
# импортируем собственные иконки
from folium.features import CustomIcon
from folium.plugins import MarkerCluster
# импортируем карту и маркер
from folium import Map, Marker
# moscow_lat - широта центра Москвы, moscow_lng - долгота центра Москвы
moscow_lat, moscow_lng = 55.751244, 37.618423
# создаём карту Москвы
m = Map(location=[moscow_lat, moscow_lng], zoom_start=10)
# создаём пустой кластер, добавляем его на карту
marker_cluster = MarkerCluster().add_to(m)
# строим карту
for key, value in icons.items():
def create_clusters(row):
# сохраняем URL-адрес изображения со значком торгового центра с icons8,
# это путь к файлу на сервере icons8
icon_url = value
# создаём объект с собственной иконкой размером 30x30
icon = CustomIcon(icon_url, icon_size=(35, 35))
# создаём маркер с иконкой icon и добавляем его в кластер
Marker(
[row['lat'], row['lng']],
popup=f"{row['name']} {row['category']} рейтинг: {row['rating']} режим работы: {row['hours']}\
расстояние до центра: {row['distance']} км",
icon=icon,
).add_to(marker_cluster)
# применяем функцию для создания кластеров к каждой строке датафрейма
data.loc[data['category']==key].apply(create_clusters, axis=1)
# выводим карту
m